#include "lift_strategy.h"

#include "application/map/lift/hk_lift.h"
#include "application/map/lift/jz_lift.h"

#include <QUuid>
#include <QString>
#include <QtGlobal>
#include <chrono>

namespace application {
namespace task {

namespace {

using Clock = std::chrono::system_clock;
using TimePoint = Clock::time_point;

constexpr std::chrono::seconds kStatusPollInterval(1);
constexpr std::chrono::seconds kCommandRetryInterval(3);
constexpr std::chrono::seconds kDoorCommandCooldown(2);
constexpr std::chrono::seconds kJzCallOpenTimeout(30);
constexpr std::chrono::seconds kTaskTimeout(600);
constexpr std::chrono::seconds kJzAimFloorArrivalDelay(3);
constexpr std::chrono::seconds kJzArrivalSettlingDelay(3);

bool isAgvAtStation(const TaskContext& ctx, const QString& station)
{
    if (!ctx.agv || station.isEmpty()) {
        return false;
    }
    return ctx.agv->getCurrentStation() == station.toStdString();
}

bool ensureAgvAtStation(LiftContext& context,
                        TaskContext& taskCtx,
                        const QString& station,
                        QString& lastNavigationTarget,
                        const std::shared_ptr<kernel::ILogger>& logger)
{
    if (station.isEmpty()) {
        return true;
    }

    if (!taskCtx.agv || !context.vehicleService) {
        return false;
    }

    if (isAgvAtStation(taskCtx, station)) {
        if (lastNavigationTarget == station) {
            lastNavigationTarget.clear();
        }
        return true;
    }

    if (lastNavigationTarget != station) {
        auto result = context.vehicleService->moveVehicleToStation(
            taskCtx.agv->getNumber(),
            "SELF_POSITION",
            station.toStdString());
        if (!result.isSuccess() && logger) {
            logger->warning(
                QString("Failed to navigate AGV %1 to station %2: %3")
                    .arg(taskCtx.agv->getNumber())
                    .arg(station)
                    .arg(result.getErrorMessage()));
        }
        lastNavigationTarget = station;
    }

    return false;
}

} // namespace

class HkLiftStrategy final : public BaseLiftStrategy {
public:
    explicit HkLiftStrategy(std::shared_ptr<kernel::ILogger> logger)
        : BaseLiftStrategy(std::move(logger)) {}

    bool init(LiftContext& context, TaskContext& taskCtx) override;
    kernel::task::TaskStage run(TaskContext& taskCtx) override;
    void cleanup(TaskContext& taskCtx) override;

private:
    void handleQueryStatus(TaskContext& ctx, TimePoint now);
    void handleCall(TaskContext& ctx, TimePoint now);
    void handleArriveCallFloor(TaskContext& ctx, TimePoint now);
    void handleInLift(TaskContext& ctx, TimePoint now);
    void handleArriveAimFloor(TaskContext& ctx, TimePoint now);
    void handleOutLift(TaskContext& ctx, TimePoint now);
    void triggerMapSwitch(TaskContext& ctx);

    LiftContext* m_context{nullptr};
    TimePoint m_startedAt{};
    TimePoint m_lastOperationTime{};
    TimePoint m_lastDoorCommandTime{};
    TimePoint m_lastStatusPoll{};
    QString m_lastNavigationTarget;
    bool m_queryIssued{false};
    bool m_callIssued{false};
    bool m_targetCallIssued{false};
    bool m_inCloseDoorIssued{false};
    bool m_outCloseDoorIssued{false};
    bool m_callDoorOpenRequested{false};
    bool m_targetDoorOpenRequested{false};
    bool m_mapSwitchIssued{false};
    bool m_statusPollIssued{false};
    bool m_timeoutNotified{false};
};

class JzLiftStrategy final : public BaseLiftStrategy {
public:
    explicit JzLiftStrategy(std::shared_ptr<kernel::ILogger> logger)
        : BaseLiftStrategy(std::move(logger)) {}

    bool init(LiftContext& context, TaskContext& taskCtx) override;
    kernel::task::TaskStage run(TaskContext& taskCtx) override;
    void cleanup(TaskContext& taskCtx) override;

private:
    void refreshJzState();
    void pollStatus(TimePoint now);
    void handleQueryStatus(TaskContext& ctx, TimePoint now);
    void handleCall(TaskContext& ctx, TimePoint now);
    void handleArriveCallFloor(TaskContext& ctx, TimePoint now);
    void handleInLift(TaskContext& ctx, TimePoint now);
    void handleArriveAimFloor(TaskContext& ctx, TimePoint now);
    void handleOutLift(TaskContext& ctx, TimePoint now);

    LiftContext* m_context{nullptr};
    TimePoint m_startedAt{};
    TimePoint m_lastOperationTime{};
    TimePoint m_lastDoorCommandTime{};
    TimePoint m_jzCallWaitStart{};
    TimePoint m_lastStatusPoll{};
    TimePoint m_jzAimFloorArrivedAt{};
    TimePoint m_jzAimFloorStageStarted{};
    QString m_lastNavigationTarget;
    bool m_queryIssued{false};
    bool m_callIssued{false};
    bool m_inCloseDoorIssued{false};
    bool m_outCloseDoorIssued{false};
    bool m_jzCallWaitActive{false};
    bool m_jzCallForceOpenSent{false};
    bool m_jzArrivalDoorOpened{false};
    bool m_jzMoveCommandIssued{false};
    bool m_mapSwitchIssued{false};
    bool m_jzTargetCallIssued{false};
    bool m_timeoutNotified{false};
    int m_jzCurrentFloor{-1};
    int m_jzDoorState{0};
};

bool HkLiftStrategy::init(LiftContext& context, TaskContext& taskCtx)
{
    Q_UNUSED(taskCtx);
    m_context = &context;
    auto now = Clock::now();
    m_startedAt = now;
    m_lastOperationTime = now;
    m_lastDoorCommandTime = now;
    m_lastStatusPoll = now;
    m_lastNavigationTarget.clear();
    m_queryIssued = false;
    m_callIssued = false;
    m_targetCallIssued = false;
    m_inCloseDoorIssued = false;
    m_outCloseDoorIssued = false;
    m_callDoorOpenRequested = false;
    m_targetDoorOpenRequested = false;
    m_mapSwitchIssued = false;
    m_statusPollIssued = false;
    m_timeoutNotified = false;
    return true;
}

kernel::task::TaskStage HkLiftStrategy::run(TaskContext& ctx)
{
    if (!m_context || !m_context->lift) {
        ctx.setError("Lift pointer lost");
        return kernel::task::TaskStage::Cleanup;
    }

    auto now = Clock::now();
    if (now - m_startedAt >= kTaskTimeout && !m_timeoutNotified) {
        m_timeoutNotified = true;
        const QString message =
            QString("HK lift task exceeded %1 seconds, continuing")
                .arg(kTaskTimeout.count());
        if (logger()) {
            logger()->warning(message);
        }
        ctx.setError(message.toStdString());
        ctx.requestStatus(kernel::task::TaskChainStatus::Timeout);
    }

    switch (m_context->lift->getStage()) {
        case lift::LiftStage::QueryStatus:
        case lift::LiftStage::QueryStatusResponse:
            handleQueryStatus(ctx, now);
            break;
        case lift::LiftStage::Call:
        case lift::LiftStage::CallResponse:
            handleCall(ctx, now);
            break;
        case lift::LiftStage::ArriveCallFloor:
        case lift::LiftStage::ArriveCallFloorResponse:
            handleArriveCallFloor(ctx, now);
            break;
        case lift::LiftStage::InCloseDoor:
        case lift::LiftStage::InCloseDoorResponse:
            handleInLift(ctx, now);
            break;
        case lift::LiftStage::ArriveAimFloor:
        case lift::LiftStage::ArriveAimFloorResponse:
            handleArriveAimFloor(ctx, now);
            break;
        case lift::LiftStage::OutCloseDoor:
        case lift::LiftStage::OutCloseDoorResponse:
            handleOutLift(ctx, now);
            break;
        case lift::LiftStage::Done:
            if (logger()) {
                logger()->info("Lift task completed");
            }
            return kernel::task::TaskStage::Cleanup;
        default:
            if (logger()) {
                logger()->warning(QString("Unknown lift stage: %1")
                                      .arg(static_cast<int>(m_context->lift->getStage())));
            }
            break;
    }

    return kernel::task::TaskStage::Running;
}

void HkLiftStrategy::cleanup(TaskContext& ctx)
{
    if (m_timeoutNotified) {
        ctx.requestStatus(kernel::task::TaskChainStatus::Ok);
        ctx.clearError();
        m_timeoutNotified = false;
    }
    m_context = nullptr;
}

void HkLiftStrategy::handleQueryStatus(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::QueryStatus) {
        if (!m_queryIssued && status == lift::LiftStatus::Idle) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_queryIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                if (m_context->lift->getStage() == lift::LiftStage::QueryStatus) {
                    m_context->lift->setStage(lift::LiftStage::QueryStatusResponse);
                }
                m_context->lift->setStatus(lift::LiftStatus::Idle);
                m_queryIssued = false;
            });
            if (logger()) {
                logger()->debug("Lift status request issued");
            }
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_queryIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::QueryStatusResponse) {
        if (status == lift::LiftStatus::Busy &&
            now - m_lastOperationTime >= kCommandRetryInterval) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }

        if (ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.waitPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStage(lift::LiftStage::Call);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_callIssued = false;
            m_lastOperationTime = now;
            if (logger()) {
                logger()->info("AGV reached lift wait point, proceeding to call lift");
            }
        }
    }
}

void HkLiftStrategy::handleCall(TaskContext&, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::Call) {
        if (!m_callIssued && status == lift::LiftStatus::Idle) {
            m_callIssued = true;
            m_lastOperationTime = now;
            m_lastStatusPoll = now;
            m_statusPollIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftCall(
                m_context->liftNum, m_context->callFloor, m_context->targetFloor, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    if (m_context->lift->getStage() == lift::LiftStage::Call) {
                        m_context->lift->setStage(lift::LiftStage::CallResponse);
                    }
                    m_context->lift->setStatus(lift::LiftStatus::Busy);
                    m_callIssued = false;
                });
            if (logger()) {
                logger()->info("Lift call request sent");
            }
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_callIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::CallResponse) {
        if (status == lift::LiftStatus::Busy &&
            now - m_lastOperationTime >= kCommandRetryInterval) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }

        if (!m_statusPollIssued && now - m_lastStatusPoll >= kStatusPollInterval) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_statusPollIssued = true;
            m_lastStatusPoll = now;
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                m_statusPollIssued = false;
            });
            if (logger()) {
                logger()->debug("HK lift: polling status while waiting for arrival");
            }
        } else if (m_statusPollIssued && now - m_lastStatusPoll >= kCommandRetryInterval) {
            m_statusPollIssued = false;
        }
    }
}

void HkLiftStrategy::handleArriveCallFloor(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::ArriveCallFloor && status == lift::LiftStatus::Idle) {
        if (!m_callDoorOpenRequested) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_callDoorOpenRequested = true;
            m_lastDoorCommandTime = now;
            if (logger()) {
                logger()->info("HK lift: request door open at call floor");
            }
        } else if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            m_callDoorOpenRequested = false;
        }

        if (ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.interPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStage(lift::LiftStage::InCloseDoor);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_inCloseDoorIssued = false;
            m_callDoorOpenRequested = false;
            if (logger()) {
                logger()->info("AGV entered lift, ready to close door");
            }
        }
        return;
    }

    if (stage == lift::LiftStage::ArriveCallFloorResponse &&
        status == lift::LiftStatus::Busy) {
        if (ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.interPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        } else if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
        }
    }
}

void HkLiftStrategy::handleInLift(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::InCloseDoor) {
        if (!ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.interPoint,
                                m_lastNavigationTarget, logger())) {
            if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
                const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
                m_context->lift->requestDoorOpen(uuid, nullptr);
                m_lastDoorCommandTime = now;
            }
            return;
        }

        if (!m_inCloseDoorIssued && status == lift::LiftStatus::Idle) {
            m_inCloseDoorIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftInLiftCloseDoor(
                m_context->liftNum, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    m_context->lift->setStage(lift::LiftStage::InCloseDoorResponse);
                    m_context->lift->setStatus(lift::LiftStatus::Idle);
                    m_inCloseDoorIssued = false;
                });
            if (logger()) {
                logger()->info("Lift door close command sent after entering lift");
            }
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_inCloseDoorIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::InCloseDoorResponse &&
        status == lift::LiftStatus::Idle) {
        if (!m_targetCallIssued) {
            m_targetCallIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);

            triggerMapSwitch(ctx);

            m_context->lift->requestLiftCall(
                m_context->liftNum,
                m_context->targetFloor,
                m_context->targetFloor,
                [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    m_context->lift->setStatus(lift::LiftStatus::Busy);
                    m_targetCallIssued = false;
                    m_lastOperationTime = Clock::now();
                });

            m_context->lift->setStage(lift::LiftStage::ArriveAimFloorResponse);
            m_lastStatusPoll = now;
            m_statusPollIssued = false;

            if (logger()) {
                logger()->info("HK lift: target floor call request sent");
            }
        }
        return;
    }

    if (stage == lift::LiftStage::InCloseDoorResponse &&
        status == lift::LiftStatus::Busy) {
        if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_targetCallIssued = false;
        }
    }
}

void HkLiftStrategy::handleArriveAimFloor(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::ArriveAimFloorResponse) {
        if (!m_statusPollIssued && now - m_lastStatusPoll >= kStatusPollInterval) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_statusPollIssued = true;
            m_lastStatusPoll = now;
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                m_statusPollIssued = false;
            });
            if (logger()) {
                logger()->debug("HK lift: polling status while heading to target floor");
            }
        } else if (m_statusPollIssued && now - m_lastStatusPoll >= kCommandRetryInterval) {
            m_statusPollIssued = false;
        }
        return;
    }

    const bool isArriveStage = (stage == lift::LiftStage::ArriveAimFloor ||
                                stage == lift::LiftStage::ArriveAimFloorResponse);
    if (isArriveStage) {
        triggerMapSwitch(ctx);
    }

    if (stage == lift::LiftStage::ArriveAimFloor && status == lift::LiftStatus::Idle) {
        if (!m_targetDoorOpenRequested) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_targetDoorOpenRequested = true;
            m_lastDoorCommandTime = now;
            if (logger()) {
                logger()->info("HK lift: request door open at target floor");
            }
        } else if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            m_targetDoorOpenRequested = false;
        }

        if (ensureAgvAtStation(*m_context, ctx, m_context->targetFloorInfo.waitPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStage(lift::LiftStage::OutCloseDoor);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_outCloseDoorIssued = false;
            m_targetDoorOpenRequested = false;
            if (logger()) {
                logger()->info("AGV leaving lift, preparing to close door");
            }
        }
        return;
    }

    if (stage == lift::LiftStage::ArriveAimFloorResponse &&
        status == lift::LiftStatus::Busy) {
        if (ensureAgvAtStation(*m_context, ctx, m_context->targetFloorInfo.waitPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        } else if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
        }
    }
}

void HkLiftStrategy::handleOutLift(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::OutCloseDoor) {
        if (!ensureAgvAtStation(*m_context, ctx, m_context->targetFloorInfo.waitPoint,
                                m_lastNavigationTarget, logger())) {
            if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
                const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
                m_context->lift->requestDoorOpen(uuid, nullptr);
                m_lastDoorCommandTime = now;
            }
            return;
        }

        if (!m_outCloseDoorIssued && status == lift::LiftStatus::Idle) {
            m_outCloseDoorIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftOutLiftCloseDoor(
                m_context->liftNum, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    if (m_context->lift->getStage() == lift::LiftStage::OutCloseDoor) {
                        m_context->lift->setStage(lift::LiftStage::Done);
                    }
                    m_context->lift->setStatus(lift::LiftStatus::Idle);
                    m_outCloseDoorIssued = false;
                });
            if (logger()) {
                logger()->info("Lift out-close-door command sent");
            }
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_outCloseDoorIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::OutCloseDoorResponse &&
        status == lift::LiftStatus::Busy &&
        now - m_lastOperationTime >= kCommandRetryInterval) {
        m_context->lift->setStatus(lift::LiftStatus::Idle);
    }
}

void HkLiftStrategy::triggerMapSwitch(TaskContext& ctx)
{
    if (m_mapSwitchIssued) {
        return;
    }

    if (!ctx.agv || !m_context || !m_context->vehicleService) {
        if (logger()) {
            logger()->warning("HK lift: cannot run switch script (missing service or AGV)");
        }
        m_mapSwitchIssued = true;
        return;
    }

    if (m_context->targetMapName.isEmpty()) {
        if (logger()) {
            logger()->warning("HK lift: target floor map name empty, skip script");
        }
        m_mapSwitchIssued = true;
        return;
    }

    auto result = m_context->vehicleService->switchMap(
        ctx.agv->getNumber(),
        m_context->targetMapName.toStdString(),
        m_context->targetFloorInfo.interPoint.toStdString());

    if (!result.isSuccess()) {
        if (logger()) {
            logger()->warning(QString("Failed to trigger HK map switch script: %1")
                                  .arg(result.getErrorMessage()));
        }
    } else if (logger()) {
        logger()->info(QString("HK lift: switch map to %1 via script")
                           .arg(m_context->targetMapName));
    }

    m_mapSwitchIssued = true;
}

bool JzLiftStrategy::init(LiftContext& context, TaskContext& taskCtx)
{
    Q_UNUSED(taskCtx);
    m_context = &context;
    auto now = Clock::now();
    m_startedAt = now;
    m_lastOperationTime = now;
    m_lastDoorCommandTime = now;
    m_jzCallWaitStart = now;
    m_lastStatusPoll = now;
    m_jzAimFloorArrivedAt = now;
    m_lastNavigationTarget.clear();
    m_callIssued = false;
    m_inCloseDoorIssued = false;
    m_outCloseDoorIssued = false;
    m_jzCallWaitActive = false;
    m_jzCallForceOpenSent = false;
    m_jzArrivalDoorOpened = false;
    m_jzMoveCommandIssued = false;
    m_mapSwitchIssued = false;
    m_jzTargetCallIssued = false;
    m_timeoutNotified = false;
    refreshJzState();
    return true;
}

kernel::task::TaskStage JzLiftStrategy::run(TaskContext& ctx)
{
    if (!m_context || !m_context->lift) {
        ctx.setError("Lift pointer lost");
        return kernel::task::TaskStage::Cleanup;
    }

    auto now = Clock::now();
    if (now - m_startedAt >= kTaskTimeout && !m_timeoutNotified) {
        m_timeoutNotified = true;
        const QString message =
            QString("JZ lift task exceeded %1 seconds, continuing")
                .arg(kTaskTimeout.count());
        if (logger()) {
            logger()->warning(message);
        }
        ctx.setError(message.toStdString());
        ctx.requestStatus(kernel::task::TaskChainStatus::Timeout);
    }

    refreshJzState();
    pollStatus(now);

    switch (m_context->lift->getStage()) {
        case lift::LiftStage::QueryStatus:
        case lift::LiftStage::QueryStatusResponse:
            handleQueryStatus(ctx, now);
            break;
        case lift::LiftStage::Call:
        case lift::LiftStage::CallResponse:
            handleCall(ctx, now);
            break;
        case lift::LiftStage::ArriveCallFloor:
        case lift::LiftStage::ArriveCallFloorResponse:
            handleArriveCallFloor(ctx, now);
            break;
        case lift::LiftStage::InCloseDoor:
        case lift::LiftStage::InCloseDoorResponse:
            handleInLift(ctx, now);
            break;
        case lift::LiftStage::ArriveAimFloor:
        case lift::LiftStage::ArriveAimFloorResponse:
            handleArriveAimFloor(ctx, now);
            break;
        case lift::LiftStage::OutCloseDoor:
        case lift::LiftStage::OutCloseDoorResponse:
            handleOutLift(ctx, now);
            break;
        case lift::LiftStage::Done:
            if (logger()) {
                logger()->info("Lift task completed");
            }
            return kernel::task::TaskStage::Cleanup;
        default:
            if (logger()) {
                logger()->warning(QString("Unknown lift stage: %1")
                                      .arg(static_cast<int>(m_context->lift->getStage())));
            }
            break;
    }

    return kernel::task::TaskStage::Running;
}

void JzLiftStrategy::cleanup(TaskContext& ctx)
{
    if (m_timeoutNotified) {
        ctx.requestStatus(kernel::task::TaskChainStatus::Ok);
        ctx.clearError();
        m_timeoutNotified = false;
    }
    m_context = nullptr;
}

void JzLiftStrategy::refreshJzState()
{
    if (!m_context) {
        return;
    }
    if (auto jzLift = qobject_cast<lift::JzLift*>(m_context->lift.get())) {
        m_jzCurrentFloor = jzLift->currentFloor();
        m_jzDoorState = jzLift->currentDoorState();
    }
}

void JzLiftStrategy::pollStatus(TimePoint now)
{
    if (!m_context || !m_context->lift) {
        return;
    }

    if (now - m_lastStatusPoll < kStatusPollInterval) {
        return;
    }

    const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
    m_lastStatusPoll = now;
    m_context->lift->requestLiftStatus(uuid, [this]() {
        if (!m_context || !m_context->lift) {
            return;
        }
        refreshJzState();
    });
}

void JzLiftStrategy::handleQueryStatus(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::QueryStatus) {
        if (!m_queryIssued && status == lift::LiftStatus::Idle) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_queryIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                if (m_context->lift->getStage() == lift::LiftStage::QueryStatus) {
                    m_context->lift->setStage(lift::LiftStage::QueryStatusResponse);
                }
                m_context->lift->setStatus(lift::LiftStatus::Idle);
                m_queryIssued = false;
            });
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_queryIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::QueryStatusResponse) {
        if (status == lift::LiftStatus::Busy &&
            now - m_lastOperationTime >= kCommandRetryInterval) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }

        if (ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.waitPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStage(lift::LiftStage::Call);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_callIssued = false;
            m_lastOperationTime = now;
        }
    }
}

void JzLiftStrategy::handleCall(TaskContext&, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::Call) {
        if (!m_callIssued && status == lift::LiftStatus::Idle) {
            m_callIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftCall(
                m_context->liftNum, m_context->callFloor, m_context->targetFloor, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    if (m_context->lift->getStage() == lift::LiftStage::Call) {
                        m_context->lift->setStage(lift::LiftStage::CallResponse);
                    }
                    m_context->lift->setStatus(lift::LiftStatus::Busy);
                    m_callIssued = false;
                });
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_callIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::CallResponse) {
        if (status == lift::LiftStatus::Busy &&
            now - m_lastOperationTime >= kCommandRetryInterval) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }

        if (!m_jzCallWaitActive) {
            m_jzCallWaitActive = true;
            m_jzCallForceOpenSent = false;
            m_jzCallWaitStart = now;
        }

        if (now - m_lastOperationTime >= kStatusPollInterval) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_lastOperationTime = now;
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                refreshJzState();
            });
        }

        if (m_jzCallWaitActive && !m_jzCallForceOpenSent &&
            now - m_jzCallWaitStart >= kJzCallOpenTimeout) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
            m_jzCallForceOpenSent = true;
            if (logger()) {
                logger()->info("JZ lift: force open after waiting 30 seconds");
            }
        }
        // TODO 临时这样 现在电梯楼层信号没有实现
        // if (m_jzCurrentFloor == m_context->callFloor && m_jzDoorState == 1) {
        if (m_jzDoorState == 1) {
            m_jzCallWaitActive = false;
            m_jzCallForceOpenSent = false;
            m_context->lift->setStage(lift::LiftStage::ArriveCallFloor);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
    }
}

void JzLiftStrategy::handleArriveCallFloor(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::ArriveCallFloor && status == lift::LiftStatus::Idle) {
        m_jzCallWaitActive = false;
        if (now - m_lastOperationTime >= kStatusPollInterval) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_lastOperationTime = now;
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                refreshJzState();
            });
        }
        if (m_jzDoorState != 1 && now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
            if (logger()) {
                logger()->info("JZ lift: retry door open at call floor");
            }
        }

        if (ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.interPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStage(lift::LiftStage::InCloseDoor);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_inCloseDoorIssued = false;
            if (logger()) {
                logger()->info("AGV entered lift, ready to close door");
            }
        }
        return;
    }

    if (stage == lift::LiftStage::ArriveCallFloorResponse &&
        status == lift::LiftStatus::Busy) {
        if (ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.interPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        } else if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
        }
    }
}

void JzLiftStrategy::handleInLift(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::InCloseDoor) {
        if (!ensureAgvAtStation(*m_context, ctx, m_context->callFloorInfo.interPoint,
                                m_lastNavigationTarget, logger())) {
            if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
                const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
                m_context->lift->requestDoorOpen(uuid, nullptr);
                m_lastDoorCommandTime = now;
            }
            return;
        }

        if (!m_jzMoveCommandIssued) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorClose(uuid, nullptr);
            m_lastDoorCommandTime = now;
            m_jzMoveCommandIssued = true;
            if (logger()) {
                logger()->info("JZ lift: closing door before moving to target floor");
            }
        }

        if (!m_inCloseDoorIssued && status == lift::LiftStatus::Idle) {
            m_inCloseDoorIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftInLiftCloseDoor(
                m_context->liftNum, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    m_context->lift->setStage(lift::LiftStage::InCloseDoorResponse);
                    m_context->lift->setStatus(lift::LiftStatus::Idle);
                    m_inCloseDoorIssued = false;
                });
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_inCloseDoorIssued = false;
            m_jzMoveCommandIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::InCloseDoorResponse &&
        status == lift::LiftStatus::Busy) {
        if (now - m_lastOperationTime >= kStatusPollInterval) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_lastOperationTime = now;
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                refreshJzState();
            });
        }
        if (m_jzDoorState == 2) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
    }

    if (stage == lift::LiftStage::InCloseDoorResponse &&
        status == lift::LiftStatus::Idle) {
        if (!m_jzTargetCallIssued) {
            m_jzTargetCallIssued = true;
            m_lastOperationTime = now;
            m_context->lift->requestLiftCall(
                m_context->liftNum,
                m_context->targetFloor,
                m_context->targetFloor,
                [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    if (logger()) {
                        logger()->info(QString("JZ lift: target floor call acknowledged -> %1")
                                           .arg(m_context->targetFloor));
                    }
                });
            if (logger()) {
                logger()->info(QString("JZ lift: request move to target floor %1")
                                   .arg(m_context->targetFloor));
            }
        }

        if (!m_mapSwitchIssued) {
            if (!m_context->vehicleService || !ctx.agv) {
                if (logger()) {
                    logger()->warning("JZ lift: cannot run switch script (missing service or AGV)");
                }
            } else if (m_context->targetMapName.isEmpty()) {
                if (logger()) {
                    logger()->warning("JZ lift: target floor map name empty, skip script");
                }
            } else {
                auto result = m_context->vehicleService->switchMap(
                    ctx.agv->getNumber(),
                    m_context->targetMapName.toStdString(),
                    m_context->targetFloorInfo.interPoint.toStdString());
                if (!result.isSuccess()) {
                    if (logger()) {
                        logger()->warning(QString("JZ lift: switch map script failed: %1")
                                              .arg(result.getErrorMessage()));
                    }
                } else if (logger()) {
                    logger()->info(QString("JZ lift: switch map triggered -> %1")
                                       .arg(m_context->targetMapName));
                }
            }
            m_mapSwitchIssued = true;
        }

        m_context->lift->setStage(lift::LiftStage::ArriveAimFloor);
        m_context->lift->setStatus(lift::LiftStatus::Busy);
        m_jzAimFloorStageStarted = now;
        m_lastOperationTime = now;
        return;
    }
}

void JzLiftStrategy::handleArriveAimFloor(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if ((stage == lift::LiftStage::ArriveAimFloor ||
         stage == lift::LiftStage::ArriveAimFloorResponse) &&
        now - m_jzAimFloorStageStarted < kJzAimFloorArrivalDelay) {
        return;
    }

    if (stage == lift::LiftStage::ArriveAimFloor) {
        if (status == lift::LiftStatus::Busy) {
            // 等待电梯完成上一个动作，定时拉取一次状态避免卡死
            if (now - m_lastOperationTime >= kStatusPollInterval) {
                const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
                m_lastOperationTime = now;
                m_context->lift->requestLiftStatus(uuid, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    refreshJzState();
                });
            }
            if (m_jzDoorState == 1) {
                m_context->lift->setStatus(lift::LiftStatus::Idle);
            }
            return;
        }

        if (now - m_lastOperationTime >= kStatusPollInterval) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_lastOperationTime = now;
            m_context->lift->requestLiftStatus(uuid, [this]() {
                if (!m_context || !m_context->lift) {
                    return;
                }
                refreshJzState();
            });
        }
        if (m_jzDoorState != 1 && now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
            if (logger()) {
                logger()->info("JZ lift: retry door open at target floor");
            }
        } else if (m_jzDoorState == 1 && !m_jzArrivalDoorOpened) {
            m_jzArrivalDoorOpened = true;
            if (logger()) {
                logger()->info("JZ lift: target floor door detected open");
            }
        }

        if (ensureAgvAtStation(*m_context, ctx, m_context->targetFloorInfo.waitPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStage(lift::LiftStage::OutCloseDoor);
            m_context->lift->setStatus(lift::LiftStatus::Idle);
            m_outCloseDoorIssued = false;
            if (logger()) {
                logger()->info("AGV leaving lift, preparing to close door");
            }
        }
        return;
    }

    if (stage == lift::LiftStage::ArriveAimFloorResponse &&
        status == lift::LiftStatus::Busy) {
        if (ensureAgvAtStation(*m_context, ctx, m_context->targetFloorInfo.waitPoint,
                               m_lastNavigationTarget, logger())) {
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        } else if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
            const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
            m_context->lift->requestDoorOpen(uuid, nullptr);
            m_lastDoorCommandTime = now;
        }
    }
}

void JzLiftStrategy::handleOutLift(TaskContext& ctx, TimePoint now)
{
    auto stage = m_context->lift->getStage();
    auto status = m_context->lift->getStatus();

    if (stage == lift::LiftStage::OutCloseDoor) {
        if (!ensureAgvAtStation(*m_context, ctx, m_context->targetFloorInfo.waitPoint,
                                m_lastNavigationTarget, logger())) {
            if (now - m_lastDoorCommandTime >= kDoorCommandCooldown) {
                const QString uuid = QUuid::createUuid().toString(QUuid::WithoutBraces);
                m_context->lift->requestDoorOpen(uuid, nullptr);
                m_lastDoorCommandTime = now;
            }
            return;
        }

        if (!m_outCloseDoorIssued && status == lift::LiftStatus::Idle) {
            m_outCloseDoorIssued = true;
            m_lastOperationTime = now;
            m_context->lift->setStatus(lift::LiftStatus::Busy);
            m_context->lift->requestLiftOutLiftCloseDoor(
                m_context->liftNum, [this]() {
                    if (!m_context || !m_context->lift) {
                        return;
                    }
                    if (m_context->lift->getStage() == lift::LiftStage::OutCloseDoor) {
                        m_context->lift->setStage(lift::LiftStage::Done);
                    }
                    m_context->lift->setStatus(lift::LiftStatus::Idle);
                    m_outCloseDoorIssued = false;
                });
            if (logger()) {
                logger()->info("Lift out-close-door command sent");
            }
        } else if (now - m_lastOperationTime >= kCommandRetryInterval) {
            m_outCloseDoorIssued = false;
            m_context->lift->setStatus(lift::LiftStatus::Idle);
        }
        return;
    }

    if (stage == lift::LiftStage::OutCloseDoorResponse &&
        status == lift::LiftStatus::Busy &&
        now - m_lastOperationTime >= kCommandRetryInterval) {
        m_context->lift->setStatus(lift::LiftStatus::Idle);
    }
}

std::unique_ptr<BaseLiftStrategy> createLiftStrategy(
    lift::LiftType type,
    std::shared_ptr<kernel::ILogger> logger)
{
    switch (type) {
        case lift::LiftType::HK:
            return std::make_unique<HkLiftStrategy>(std::move(logger));
        case lift::LiftType::JZ:
            return std::make_unique<JzLiftStrategy>(std::move(logger));
        default:
            return nullptr;
    }
}

} // namespace task
} // namespace application
